/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { XButton } = require("../../../../engine/control/XButton");
const { X2DBeta } = require("../../../../engine/graphics/X2DInterface");
const { Scr } = require("../../../../engine/XDefine");
const { TextEditor } = require("../../../../engine/XTextInput");
const { XTools } = require("../../../../engine/XTools");
const { PackageTool } = require("../../../ros_msg_utils/PackageTool");
const { RosProto } = require("../../../ros_msg_utils/RosProto");
const { WsClient2 } = require("../../../../engine/ws/WsClient2");
const { EnvManager } = require("../../EnvManager");
const { LogManager } = require("../../LogManager");
const { ProjectConfig } = require("../../ProjectConfig");
const { RosBase } = require("./RosBase");
const DefaultRenderFunc = require("../DefaultRenderFunc");
require("../../../render/RosRender");

window.topic = {
    publish: (topicName, msg, delay = 1000) => {
        const { RuntimeManager } = require("../RuntimeManager");
        if (!(topicName in RuntimeManager.getInstance().topics)) {
            LogManager.E("~~~ 发布错误: " + topicName + "未找到该话题 ~~~");
            return false;
        }
        let msgType = RuntimeManager.getInstance().topics[topicName].msgType;
        if (!msgType) {
            RosProto.queryTopicMsgType(topicName);
            RuntimeManager.getInstance().topics[topicName].msgType = "querying..."
            LogManager.I("正在查询话题消息类型: " + topicName);
            return false;
        } else if (msgType == "querying...") {
            LogManager.I("查询中...");
            return false;
        }
        let msg_obj = EnvManager.getInstance().getMsgObj(msgType);
        if (!msg_obj) {
            LogManager.E("~~~ 发布错误: " + topicName + "未找到消息定义 ~~~");
            return false;
        }
        let pt = new PackageTool();
        pt.put_string("publish_topic");
        pt.put_string(topicName);
        pt.put_uint8(1);// 发布
        {
            msg_obj.put_msg(pt, msg);
            if (delay <= 0) {
                pt.put_float32(0);
            } else {
                pt.put_float32(1000 / delay);
            }
        }
        WsClient2.send(pt);
        RuntimeManager.getInstance().topics[topicName].isPublishable = true;
        RuntimeManager.getInstance().topics[topicName].btn_publish.name_ = "取消发布";
        return true;
    },
    unpublish: (topicName) => {
        const { RuntimeManager } = require("../RuntimeManager");
        if (!(topicName in RuntimeManager.getInstance().topics)) {
            LogManager.E("~~~ 取消发布错误: " + topicName + "未找到该话题 ~~~");
            return;
        }
        let pt = new PackageTool();
        pt.put_string("publish_topic");
        pt.put_string(topicName);
        pt.put_uint8(0);// 取消发布
        WsClient2.send(pt);
    },
    lastMessage: (topicName) => {
        const { RuntimeManager } = require("../RuntimeManager");
        if (!(topicName in RuntimeManager.getInstance().topics)) {
            LogManager.E("~~~ 获取错误: " + topicName + "未找到该话题 ~~~");
            return null;
        }
        return RuntimeManager.getInstance().topics[topicName].msg;
    },
}
class RosTopic extends RosBase {
    constructor(name) {
        super(name, "topic");
        this.msgType = null;
        this.isSubscribed = false;
        this.isPublishable = false;
        this.msgReceived = 0;
        this.msg = null;
        this.msgId = -1;

        this.btn_subscribe = new XButton({
            name: "订阅",
            onClicked: () => {
                if (this.isSubscribed) {
                    RosProto.subscribeTopic(this.name, 0);//取消订阅
                    this.isSubscribed = false;
                    this.btn_subscribe.name_ = "订阅";
                } else {
                    RosProto.subscribeTopic(this.name, 1);//订阅
                    this.isSubscribed = true;
                    this.msgReceived = 0;
                    this.msgId = -1;
                    this.msg = null;
                    this.asyncData = {};
                    this.btn_subscribe.name_ = "取消订阅";
                }
            },
            onDraw: (offx, offy) => {
                this.renderButton(this.btn_subscribe, offx, offy);
            }
        })
        this.btn_subscribe_func = new XButton({
            name: "订阅方法",
            onClicked: () => {
                this.onDoubleClicked(0, 0);
            },
            onDraw: (offx, offy) => {
                this.renderButton(this.btn_subscribe_func, offx, offy);
            }
        });
        this.btn_publish = new XButton({
            name: "发布",
            onClicked: () => {
                if (!this.isPublishable) {
                    try {
                        let func = eval(this.publishFunc)
                        func();
                    } catch (e) {
                        LogManager.E("~~~ 发布错误 ~~~");
                        LogManager.E("Type:", this.type);
                        LogManager.E("Name:", this.name);
                        LogManager.E("Error:", e.message);
                        // LogManager.I(e.stack);
                        LogManager.E("~~~~ 已取消发布 ~~~");
                        topic.unpublish(this.name);
                        this.isPublishable = false;
                        this.btn_publish.name_ = "发布";
                    }
                } else {
                    topic.unpublish(this.name);
                    this.isPublishable = false;
                    this.btn_publish.name_ = "发布";
                }
            },
            onDraw: (offx, offy) => {
                this.renderButton(this.btn_publish, offx, offy);
            }
        });
        this.btn_publish_func = new XButton({
            name: "发布方法",
            onClicked: () => {
                this.openPublishFuncEditor();
            },
            onDraw: (offx, offy) => {
                this.renderButton(this.btn_publish_func, offx, offy);
            }
        });
    }
    onDraw(offx = 0, offy = 0, locked = false) {
        let x = this.x + offx;
        let y = this.y + offy;
        let neww = 200;
        X2DBeta.fillRect(x, y, this.w, 16, 0xff303030);
        X2DBeta.drawText("topic" + (this.isSubscribed ? ["-", "/", "|", "\\"][this.msgReceived % 4] : ""), 12, x + this.w / 2, y + 8, 1, 1, 0, -2, -2, 0xfff1c40f);
        // X2DBeta.drawText("topic" + (this.isSubscribed ? "(" + this.msgReceived + ")" : ""), 12, x + this.w / 2, y + 8, 1, 1, 0, -2, -2, 0xfff1c40f);
        y += 16;

        X2DBeta.fillRect(x, y, this.w, this.h - 16, 0xff404040);
        neww = Math.max(neww, X2DBeta.drawText(this.name, 12, x + 4, y + 8, 1, 1, 0, -1, -2, 0xffe0e0e0) + 8);
        y += 16;

        if (this.msgType) {
            // X2DBeta.fillRect(x, y, this.w, 16, 0xff404040);
            neww = Math.max(neww, X2DBeta.drawText(" Type:" + this.msgType, 12, x + 4, y + 8, 1, 1, 0, -1, -2, 0xffe0e0e0) + 8);
            y += 16;
        }

        if (this.isSubscribed) {
            // X2DBeta.fillRect(x, y, this.w, 16, 0xff404040);
            // let w = X2DBeta.drawText(" Receiving:", 12, x + 4, y + 8, 1, 1, 0, -1, -2, 0xffe0e0e0);
            // X2DBeta.drawText("" + this.msgReceived, 12, x + 4 + w, y + 8, 1, 1, 0, -1, -2, 0xffe0e0e0);
            // y += 16;
            if (this.msg) {
                let funcStr = "";
                if (this.renderFunc && this.renderFunc.length > 0) {
                    funcStr = this.renderFunc;
                } else {
                    let fs = DefaultRenderFunc.getRenderFunc(this.msgType);
                    if (fs) {
                        funcStr = `()=>{
                            let msg = topic.lastMessage("${this.name}");
                            //TODO: 调用render API可视化订阅的消息
                        ${fs}
                        }`;
                    }
                }
                if (funcStr.length > 0) {
                    this.asyncData["changed"] = this.msgReceived != this.msgId;
                    render.reset(x, y, neww, this.asyncData);
                    try {
                        let func = eval(funcStr)
                        func(this.msg);
                        render.log_flush();
                        this.msgId = this.msgReceived;
                    } catch (e) {
                        LogManager.E("~~~ 渲染函数错误 ~~~");
                        LogManager.E("Type:", this.type);
                        LogManager.E("Name:", this.name);
                        LogManager.E("Error:", e.message);
                        // LogManager.I(e.stack);
                        LogManager.E("~~~~ 已取消订阅 ~~~");
                        RosProto.subscribeTopic(this.name, 0);//取消订阅
                        this.isSubscribed = false;
                    }
                    y += render.MAXH;
                    neww = Math.max(neww, render.MAXW);
                }
            }
        }
        if (neww != this.w) {
            this.w = neww;
            XTools.RUNTIME_UPDATE_LINES = true;
        }
        let newh = y - offy - this.y;
        if (newh != this.h) {
            this.h = newh;
            XTools.RUNTIME_UPDATE_LINES = true;
        }

        if (locked) {
            this.btns = [this.btn_hide, this.btn_subscribe, this.btn_subscribe_func];
            for (let i = 0; i < this.btns.length; i++) {
                this.btns[i].move(this.x + this.w, this.y + i * 16, 60, 14).onDraw(offx, offy);
            }
            this.btns.push(this.btn_publish, this.btn_publish_func);
            for (let i = 0; i < 2; i++) {
                this.btns[i + 3].move(this.x - 60, this.y + i * 16, 60, 14).onDraw(offx, offy);
            }
            // this.btn_subscribe.move(this.x + 1, this.y + 1).onDraw(offx, offy);
            // this.btn_hide.move(this.x + this.w - this.btn_hide.width() - 1, this.y + 1).onDraw(offx, offy);
        }
    }
    onSubscribe(msg) {//收到订阅的消息
        this.msgReceived += 1;
        this.msg = msg;
    }
    openPublishFuncEditor() {
        let ss = `/*
消息格式：
msg:${this.msgType}
`
        let msg_obj = EnvManager.getInstance().getMsgObj(this.msgType);
        if (msg_obj) {
            ss += msg_obj.dump_struct("msg.", 1);
        }

        ss += `
发布API:
    topic.publish(name, msg, delay = 1000);//发布消息,msg为消息对象,delay为延迟时间(ms)
*/\n`;
        let defaultPublicFunc = `()=>{
    //TODO: 填充要发布的消息内容
    let msg = ${msg_obj.mock_js_msg()};
    topic.publish("${this.name}", msg, 1000);
}`
        if (!this.publishFunc || this.publishFunc.length == 0) {
            this.publishFunc = defaultPublicFunc;
        }
        TextEditor.CodeTextInput.Reset({
            x: XTools.MOUSE_POS.x > Scr.logicw / 2 ? 0 : parseInt(Scr.logicw / 2),
            y: 0,
            w: Scr.logicw / 2,
            h: Scr.logich,
            defaultValue: ss + this.publishFunc,
            onChange: (value) => { },
            onFinish: (value) => {
                let t = value.substring(ss.length);
                if (t != this.publishFunc) {
                    this.publishFunc = t;
                    ProjectConfig.getInstance().setRuntimeAttr(this);
                    if (this.isPublishable) {
                        try {
                            let func = eval(this.publishFunc)
                            func();
                        } catch (e) {
                            LogManager.E("~~~ 发布错误 ~~~");
                            LogManager.E("Type:", this.type);
                            LogManager.E("Name:", this.name);
                            LogManager.E("Error:", e.message);
                            // LogManager.I(e.stack);
                            LogManager.E("~~~~ 已取消发布 ~~~");
                            topic.unpublish(this.name);
                            this.isPublishable = false;
                            this.btn_publish.name_ = "发布";
                        }
                    }
                }
            },
        });
    }
    onDoubleClicked(x, y) {//双击打开编辑可视化源码
        let ss = `/*
消息格式：
msg:${this.msgType}
`
        let msg_obj = EnvManager.getInstance().getMsgObj(this.msgType);
        if (msg_obj) {
            ss += msg_obj.dump_struct("msg.", 1);
        }
        ss += `
渲染API:
    render.text(text, color = 0xffffffff);//渲染单行文本
    render.imageRgb(dataRgb, width, height, scale = 1);//渲染图像,dataRgb为二进制rgb数据
    render.imageJpg(dataJpg, scale = 1);//渲染图像,dataJpg为二进制jpg数据
    render.log(...);//渲染日志
*/\n`

        let defaultRenderFunc = `()=>{
    let msg = topic.lastMessage("${this.name}");
    //TODO: 调用render API可视化订阅的消息
${DefaultRenderFunc.getRenderFunc(this.msgType)}
}`;
        if (this.renderFunc && this.renderFunc.length > 0) {
            defaultRenderFunc = this.renderFunc;
        }

        TextEditor.CodeTextInput.Reset({
            x: XTools.MOUSE_POS.x > Scr.logicw / 2 ? 0 : parseInt(Scr.logicw / 2),
            y: 0,
            w: Scr.logicw / 2,
            h: Scr.logich,
            defaultValue: ss + defaultRenderFunc,
            onChange: (value) => { },
            onFinish: (value) => {
                if (value.length == 0) {
                    this.renderFunc = "";
                }
                else if (value.substring(0, ss.length) != ss) {
                    LogManager.E("~~~ 提示部分被修改，渲染函数未保存 ~~~");
                } else {
                    let t = value.substring(ss.length);
                    if (t != defaultRenderFunc) {
                        this.renderFunc = t;
                        ProjectConfig.getInstance().setRuntimeAttr(this);
                    }
                }
            },
        });
    }
}

module.exports = { RosTopic };